package com.cloudlead.common.worker.action.impl;

import com.cloudlead.common.lang.Messages;
import com.cloudlead.common.lang.api.ValidatorError;
import com.cloudlead.common.lang.exception.RequestNotAcceptableException;
import com.cloudlead.common.lang.serialize.BeanSerializeException;
import com.cloudlead.common.lang.serialize.JSON;
import com.cloudlead.common.worker.action.ActionFactory;
import com.cloudlead.common.worker.annotation.ValidatorGroup;
import com.cloudlead.common.worker.utils.MethodParamsNameHelper;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.NotNull;
import javax.validation.groups.Default;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

public class DefaultSimpleAction extends AbstractSimpleAction {

    protected final ActionFactory factory;

    protected final Method processMethod;

    public DefaultSimpleAction(ActionFactory factory, Method processMethod) {
        super();
        this.factory = factory;
        this.processMethod = processMethod;
    }

    @Override
    public Object process(Map<String, Object> request) throws Exception {
        //检查模块是否启用
        if (!factory.isEnabled()) {
            throw new RequestNotAcceptableException(Messages.getMessage("worker.action.module.disable"));
        }
        if (null != processMethod) {
            ValidatorFactory validatorFactory = Validation
                    .buildDefaultValidatorFactory();
            Validator validator = validatorFactory.getValidator();

            // 检查Action类上是否有验证组定义
            ValidatorGroup validatorGroups = processMethod
                    .getAnnotation(ValidatorGroup.class);
            Class<?>[] groups = null;
            if (null != validatorGroups) {
                groups = validatorGroups.value();
            }
            if (null == groups || groups.length == 0) {
                groups = new Class<?>[]{Default.class};
            } else {
                List<Class<?>> list = new ArrayList<>();
                list.addAll(Arrays.asList(groups));
                list.add(Default.class);
                groups = list.toArray(new Class<?>[list.size()]);
            }

            List<ValidatorError> messages = new ArrayList<>();

            Parameter[] parameters = processMethod.getParameters();
            String[] paramNames = MethodParamsNameHelper.getMethodParamNames(processMethod);
            Object[] params = new Object[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                String name = paramNames[i];
                Object object = request.get(name);
                params[i] = object;

                NotNull notNull = parameters[i].getAnnotation(NotNull.class);
                if (null != notNull && null == object) {
                    ValidatorError message = new ValidatorError();
                    message.setMessage(Messages.getMessage("worker.action.validator.NotNull"));
                    message.setPropertyPath(name);
                    message.setMessageTemplate("{javax.validation.constraints.NotNull.message}");
                    message.setRootBeanClass(factory.getClass().getName() + "." + processMethod.getName());
                    message.setInvalidValue(null);
                    messages.add(message);
                }
                if (null != object) {
                    Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
                    if (null != constraintViolations && constraintViolations.size() > 0) {
                        for (ConstraintViolation<Object> constraintViolation : constraintViolations) {
                            ValidatorError message = new ValidatorError();
                            message.setMessage(constraintViolation.getMessage());
                            message.setPropertyPath(constraintViolation.getPropertyPath()
                                    .toString());
                            message.setMessageTemplate(constraintViolation
                                    .getMessageTemplate());
                            message.setRootBeanClass(constraintViolation.getRootBeanClass()
                                    .getName());
                            message.setInvalidValue(constraintViolation.getInvalidValue());
                            messages.add(message);
                        }
                    }
                }
            }


            if (!messages.isEmpty()) {
                String msg = null;
                try {
                    msg = JSON.toString(messages);
                } catch (BeanSerializeException e) {
                    e.printStackTrace();
                }
                throw new RequestNotAcceptableException(msg);
            }

            try {
                return processMethod.invoke(factory, params);
            } catch (IllegalAccessException | IllegalArgumentException e) {
                e.printStackTrace();
                throw new RequestNotAcceptableException(Messages.getMessage("worker.action.method.IllegalArgument"), e);
            } catch (InvocationTargetException e) {
                e.printStackTrace();
                throw (Exception) e.getTargetException();
            }
        }
        return null;
    }

}
